﻿
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class MousePointerEvent : MonoBehaviour
{

    private Vector2 m_LastMousePosition = Vector2.zero;
    private GameObject m_PointerEnter = null;

    private MyPointerEventData[] m_PointerData = new MyPointerEventData[3];
    private List<MyPointerEventData> m_ActivePointerDataList = new List<MyPointerEventData>();
    private List<MyPointerEventData> m_TempRemovePointerDataList = new List<MyPointerEventData>();

    private List<RaycastResult> m_RaycastResultCache = new List<RaycastResult>();
    private PointerEventData m_TempPointerEventData;

    private GameObject m_FirstRaycastGameObject = null;
    private int m_FrameInvokeCount;

    private int m_UpdateIndex;

    protected void OnPointerEnter(MyPointerEventData data)
    {
        Debug.Log($"OnPointerEnter");
    }

    protected void OnPointerExit(MyPointerEventData data)
    {
        Debug.Log($"OnPointerExit");
    }

    protected void OnPointerDown(MyPointerEventData data)
    {
        Debug.Log($"OnPointerDown");
    }

    protected void OnPointerUp(MyPointerEventData data)
    {
        Debug.Log($"OnPointerUp");
    }

    protected void OnClick(MyPointerEventData data)
    {
        Debug.Log($"OnClick");
    }

    protected void OnLongPressBegin(MyPointerEventData data)
    {
        Debug.Log($"OnLongPressBegin");
    }

    protected void OnLongPress(MyPointerEventData data)
    {
        Debug.Log($"OnLongPress");
    }

    protected void OnLongPressEnd(MyPointerEventData data)
    {
        Debug.Log($"OnLongPressEnd");
    }

    protected void OnDragBegin(MyPointerEventData data)
    {
        Debug.Log($"OnDragBegin");
    }

    protected void OnDrag(MyPointerEventData data)
    {
        //Debug.Log($"OnDrag");
    }

    protected void OnDragEnd(MyPointerEventData data)
    {
        Debug.Log($"OnDragEnd");
    }

    void Update()
    {
        m_FirstRaycastGameObject = null;
        m_FrameInvokeCount = 0;

        Vector2 mousePos = Input.mousePosition;
        Vector2 delta = mousePos - m_LastMousePosition;
        if (0 == m_UpdateIndex || delta.sqrMagnitude >= 0.001f) //移动: 没发生移动就不需要检测Enter对象变化
        {
            m_LastMousePosition = mousePos;

            var firstRaycastGameObject = GetFirstRaycastGameObject(Input.mousePosition);
            if (null == m_PointerEnter)
            {
                if (null == firstRaycastGameObject)
                {
                    //todo: hovered上触发OnPoiterExit
                }
                else
                {
                    //todo: 新的触发OnPointerEnter
                    m_PointerEnter = firstRaycastGameObject;
                }
            }
            else
            {
                if (null == firstRaycastGameObject)
                {
                    //todo: hovered上触发OnPoiterExit
                    m_PointerEnter = null;
                }
                else if (firstRaycastGameObject != m_PointerEnter)
                {
                    //todo: 旧的触发OnPointerEixt, 新的触发OnPointerEnter
                    m_PointerEnter = firstRaycastGameObject;
                }
            }
        }

        CheckMouseDown(0);
        CheckMousePressed(0);
        CheckMouseUp(0);

        //todo: 解析所有

        foreach (var data in m_TempRemovePointerDataList)
        {
            m_ActivePointerDataList.Remove(data);
        }

        //Thread.Sleep(1000);
        m_UpdateIndex++;
    }

    private void CheckMouseDown(int button)
    {
        if (Input.GetMouseButtonDown(button)) //down
        {
            //Debug.Log($"mouse button down: {button}, {m_UpdateIndex}");

            var pointerData = GetPointerData(button);
            pointerData.m_Position = Input.mousePosition;
            pointerData.m_Delta = Vector2.zero;
            //scrollDelta, button
            pointerData.m_Status = MyPointerEventData.Status.None;

            pointerData.m_DownPosition = pointerData.m_Position;
            var newDown = GetFirstRaycastGameObject(Input.mousePosition);
            OnPointerDown(pointerData); //todo: down对象触发pointerDown

            if (newDown == pointerData.m_LastDown)
            {
                float elapsedTime = Time.unscaledTime - pointerData.m_DownTime;
                if (elapsedTime < 0.3f)
                    pointerData.m_ClickCount++;
                else //超过时间就直接重置
                    pointerData.m_ClickCount = 1;
            }
            else //第1次Down或换了一个GameObject Down
            {
                pointerData.m_ClickCount = 1;
            }
            pointerData.m_DownTime = Time.unscaledTime;

            pointerData.m_PointerDown = newDown;
            pointerData.m_PointerDrag = newDown;
            //todo: drag对象触发OnPotentialDrag
        }
    }

    private void CheckMousePressed(int button)
    {
        if (Input.GetMouseButton(button)) //按着
        {
            //Debug.Log($"mouse button: {button}, {m_UpdateIndex}");

            var pointerData = GetPointerData(button);
            Vector2 lastPosition = pointerData.m_Position;
            pointerData.m_Position = Input.mousePosition;
            pointerData.m_Delta = pointerData.m_Position - lastPosition;
            //scrollDelta, button

            if (pointerData.m_Delta.sqrMagnitude > 0 && null != pointerData.m_PointerDrag)
            {
                switch (pointerData.m_Status)
                {
                case MyPointerEventData.Status.None:
                {
                    float dragSlop = 7;
                    Vector2 deltaPos = pointerData.m_DownPosition - pointerData.m_Position;
                    if (deltaPos.sqrMagnitude >= dragSlop * dragSlop)
                    {
                        OnDragBegin(pointerData); //todo: drag对象触发OnBeginDrag
                        pointerData.m_Status = MyPointerEventData.Status.Drag;

                        if (pointerData.m_PointerDown != pointerData.m_PointerDrag)
                        {
                            //todo: down对象触发OnPointerUp
                            //pointerData.m_PointerDown = null;
                        }
                        OnDrag(pointerData); //todo: drag对象触发OnDrag
                    }
                    else
                    {
                        float elapsedTime = Time.unscaledTime - pointerData.m_DownTime;
                        if (elapsedTime >= 0.3f)
                        {
                            OnLongPressBegin(pointerData); //todo: down对象触发OnLongPressBegin
                            pointerData.m_Status = MyPointerEventData.Status.LongPress;
                        }
                    }
                }
                break;

                case MyPointerEventData.Status.LongPress:
                {
                    float dragSlop = 7;
                    Vector2 deltaPos = pointerData.m_DownPosition - pointerData.m_Position;
                    if (deltaPos.sqrMagnitude >= dragSlop * dragSlop)
                    {
                        OnDragBegin(pointerData); //todo: drag对象触发OnBeginDrag
                        pointerData.m_Status = MyPointerEventData.Status.Drag;

                        if (pointerData.m_PointerDown != pointerData.m_PointerDrag)
                        {
                            //todo: down对象触发OnPointerUp
                            //pointerData.m_PointerDown = null;
                        }
                        OnDrag(pointerData); //todo: drag对象触发OnDrag
                    }
                    else
                    {
                        OnLongPress(pointerData); //todo: down对象触发OnLongPress
                    }
                }
                break;

                case MyPointerEventData.Status.Drag:
                {
                    OnDrag(pointerData); //todo: drag对象触发OnDrag
                }
                break;

                }
            }
        }
    }

    private void CheckMouseUp(int button)
    {
        if (Input.GetMouseButtonUp(button)) //抬起
        {
            //Debug.Log($"mouse button up: {button}, {m_UpdateIndex}");

            var pointerData = GetPointerData(button);
            Vector2 lastPosition = pointerData.m_Position;
            pointerData.m_Position = Input.mousePosition;
            pointerData.m_Delta = pointerData.m_Position - lastPosition;
            //scrollDelta, button

            var firstRaycastGameObject = GetFirstRaycastGameObject(Input.mousePosition);
            switch (pointerData.m_Status)
            {
            case MyPointerEventData.Status.None:
            {
                OnPointerUp(pointerData); //todo: down对象触发OnPointerUp

                if (pointerData.m_PointerDown == firstRaycastGameObject)
                {
                    OnClick(pointerData); //todo: down对象触发OnClick事件, 检查clickCount
                }
                pointerData.m_LastDown = pointerData.m_PointerDown; //用于下次Down检测双击
            }
            break;

            case MyPointerEventData.Status.Drag:
            {
                //在前面已经触发过OnPointerUp

                if (null != pointerData.m_PointerDrag)
                {
                    OnDragEnd(pointerData); //todo: drag对象触发OnDragEnd
                }
                pointerData.m_LastDown = null; //drag, 下次Down没双击
            }
            break;

            case MyPointerEventData.Status.LongPress:
            {
                OnPointerUp(pointerData); //todo: down对象触发OnPointerUp

                if (pointerData.m_PointerDown == firstRaycastGameObject)
                {
                    OnLongPressEnd(pointerData); //todo: 触发OnLongPressEnd事件
                }
                pointerData.m_LastDown = pointerData.m_PointerDown; //用于下次Down检测双击
            }
            break;
            }

            pointerData.m_PointerDown = null;
            pointerData.m_PointerDrag = null;
            pointerData.m_Status = MyPointerEventData.Status.None;

            if (m_PointerEnter != firstRaycastGameObject)
            {
                //todo: 在旧Enter节点上触发一次Exit
                //todo: 在抬起节点上触发一次Enter
            }

            m_TempRemovePointerDataList.Add(pointerData);
        }
    }

    private MyPointerEventData GetPointerData(int pid)
    {
        var pointerData = m_PointerData[pid];
        if (null == pointerData)
        {
            pointerData = new MyPointerEventData(pid);
            m_PointerData[pid] = pointerData;
        }

        m_ActivePointerDataList.Add(pointerData);
        return pointerData;
    }

    protected GameObject GetFirstRaycastGameObject(Vector2 position)
    {
        m_FrameInvokeCount++;
        if (m_FrameInvokeCount > 1)
            return m_FirstRaycastGameObject;

        var evtSystem = EventSystem.current;
        if (null == m_TempPointerEventData)
            m_TempPointerEventData = new PointerEventData(evtSystem);

        m_TempPointerEventData.position = new Vector2(position.x, position.y);
        evtSystem.RaycastAll(m_TempPointerEventData, m_RaycastResultCache);

        if (m_RaycastResultCache.Count > 0)
        {
            for (int i = 0; i < m_RaycastResultCache.Count; ++i)
            {
                var go = m_RaycastResultCache[i].gameObject;
                if (null != go)
                {
                    m_FirstRaycastGameObject = go;
                    break;
                }
            }
            m_RaycastResultCache.Clear();
        }

        return m_FirstRaycastGameObject;
    }

}
